﻿using System;
using System.Collections.Generic;
using System.Text;
using Rhino.DSL;
using NLog;
using System.IO;
using B=Boo.Lang;
using System.Reflection;

namespace NGinn.RippleBoo
{
    public class RuleRepository<TBase> where TBase : RuleSetBase
    {
        private string _baseDir;
        private Logger log = LogManager.GetCurrentClassLogger();

        public string BaseDirectory
        {
            get { return _baseDir; }
            set { _baseDir = value; }
        }

       
        private DslFactory _factory = new DslFactory();

        public RuleRepository()
        {
            
        }

        private List<string> _namespaces = new List<string>();
        public IList<string> ImportNamespaces
        {
            get { return _namespaces; }
        }

        public bool AutoDetectFileModifications { get; set; }

        private bool _inited = false;
        protected void Initialize()
        {
            if (_inited) return;
            log.Info("Initializig RuleRepository in {0} for base type {1}", BaseDirectory, typeof(TBase).Name);
            RippleBooDslEngine<TBase> eng = new RippleBooDslEngine<TBase>(AutoDetectFileModifications, delegate(string url)
            {
                string s = System.IO.Path.GetFullPath(System.IO.Path.Combine(BaseDirectory, url));
                return s;
            });
            eng.Namespaces = _namespaces.ToArray();
            _factory.Register<TBase>(eng);
            _factory.BaseDirectory = BaseDirectory;
            _factory.Compilation += new EventHandler(_factory_Compilation);
            _factory.Recompilation += new EventHandler(_factory_Recompilation);
            _inited = true;
        }

        void _factory_Recompilation(object sender, EventArgs e)
        {
            log.Warn("script ReCompilation in {0}", _factory.BaseDirectory);
        }

        void _factory_Compilation(object sender, EventArgs e)
        {
            log.Warn("script Compilation in {0}", _factory.BaseDirectory);
        }

        protected TBase GetNewRuleSet(string name)
        {
            Initialize();
            var rb = _factory.Create<TBase>(name);
            rb.Name = name;
            return rb;
        }

        protected TBase[] GetAllRuleSets()
        {
            Initialize();
            var rbs = _factory.CreateAll<TBase>("");
            return rbs;

        }

        public void EvaluateRules(string ruleset, IDictionary<string, object> variables, IDictionary<string, object> context)
        {
            EvaluateRules(ruleset, new QuackWrapper(variables), new QuackWrapper(context));
        }

        public void EvaluateRules(string ruleset, B.IQuackFu variables, B.IQuackFu context)
        {
            try
            {
                DateTime ds = DateTime.Now;
                RuleSetBase rb = GetNewRuleSet(ruleset);
                rb.Variables = variables;
                rb.Context = context;
                rb.Initialize();
                rb.Execute();
                log.Info("Rule evaluation time: {0}", DateTime.Now - ds);
                if (rb._gotoRulesFile != null)
                {
                    log.Info("Evaluating included ruleset {1}", rb._gotoRulesFile);
                    EvaluateRules(rb._gotoRulesFile, variables, context);
                }
            }
            catch (Exception ex)
            {
                log.Error("Rule evaluation exception in ruleset {0}: {1}", ruleset, ex);
                throw ex;
            }
        }

        public void EvaluateAllRulesets(IDictionary<string, object> variables, IDictionary<string, object> context)
        {
            EvaluateAllRulesets(new QuackWrapper(variables), new QuackWrapper(context));
        }


        public void EvaluateAllRulesets(B.IQuackFu variables, B.IQuackFu context)
        {
            string ruleset = null;
            try
            {
                DateTime ds = DateTime.Now;
                var rbs = GetAllRuleSets();
                foreach (var rb in rbs)
                {
                    ruleset = rb.GetType().Name;
                    rb.Variables = variables;
                    rb.Context = context;
                    rb.Initialize();
                    rb.Execute();
                    if (rb._gotoRulesFile != null)
                    {
                        log.Info("Evaluating included ruleset {1}", rb._gotoRulesFile);
                        EvaluateRules(rb._gotoRulesFile, variables, context);
                    }
                }
                log.Info("Rule evaluation time: {0}", DateTime.Now - ds);
            }
            catch (Exception ex)
            {
                log.Error("Rule evaluation exception in ruleset {0}: {1}", ruleset, ex);
                throw ex;
            }
        }

        public void SaveRuleGraph(string rulesFile, string ruleset, string fileName)
        {
            using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8))
            {
                RuleSetBase rsb = GetNewRuleSet(rulesFile);
                rsb.Initialize();
                rsb.ToGraph(sw, ruleset);
            }
        }

        
    }
}
